/*
 *  WaveTableFactory.h
 *  SoundController0002
 *
 *  Created by jiro on 10/06/29.
 *  Copyright 2010 jiro music. All rights reserved.
 *
 */

#pragma once

#include "WaveTable.h"
#include "SineWaveTable.h"
#include "ConstantWaveTable.h"
#include "NullWaveTable.h"
#include "WaveTableWithArray.h"
#include "WaveTableWithWaveFunction.h"
#include "WaveFunctionFactory.h"
#include "AllExceptions.h"
#include <vector>

namespace sc 
{
	class WaveTableFactory 
	{
	private:
	
		static std::vector< const WaveTable* > waveTables;
		
		static std::vector< std::vector< double > > valuesVector;

	public:

		static const WaveTable& Create( WaveTable::Type type, double frequency, double adjuster = 1.0 )
		{
			WaveTable* table;
			SineWaveTable sineWaveTable = SineWaveTable( frequency, 1.0, adjuster );
			ConstantWaveTable constantWaveTable = ConstantWaveTable( frequency );
			switch ( type ) {
			case WaveTable::Type_Sine:
				table = & sineWaveTable;
				break;
			case WaveTable::Type_Constant:
				table = & constantWaveTable;
				break;
			case WaveTable::Type_Array:
				throw Exception( "invalid type" );
				break;
			default:
				return *NullWaveTable::GetInstance();
			}
			
			int count = waveTables.size();
			for ( int i = 0; i < count; i++ )
			{
				if ( *(waveTables[ i ]) == *table )
				{
					return * waveTables[ i ];
				}
			}

			switch ( type ) {
			case WaveTable::Type_Sine:
				table = new SineWaveTable( frequency, 0, adjuster );
				break;
			case WaveTable::Type_Constant:
				table = new ConstantWaveTable( frequency );
				break;
			}
			waveTables.push_back( table );
			return * table;
		}
		
		static const WaveTable& CreateSin( double frequency, double phaseShift, double adjuster )
		{
			const WaveTable* tablePtr = Get( SineWaveTable( frequency, phaseShift, adjuster ) );
			if ( tablePtr != NULL  ) {
				return * tablePtr;
			}
			tablePtr = new SineWaveTable( frequency, phaseShift, adjuster );
			waveTables.push_back( tablePtr );
			return * tablePtr;	
		}

		static const WaveTable& Create( const std::vector< double >& values )
		{
			std::stringstream ss;
			ss << __FILE__ << " " << __LINE__ << " " << __FUNCTION__;
			throw NotImplementedException( ss.str() );
//			size_t count = waveTables.size();
//			const WaveTableWithArray tableWithArray( values );
//			for ( size_t i = 0; i < count; i++ ) {
//				const WaveTableWithArray* table = dynamic_cast< const WaveTableWithArray* >( waveTables[ i ] );
//				if ( table == NULL ) {
//					continue;
//				}
//				if ( *table == tableWithArray ) {
//					return * table;
//				}
//			}
//			valuesVector.push_back( values );
//
//			const WaveTableWithArray* table = new WaveTableWithArray( & valuesVector.back() );
//			waveTables.push_back( table );
//			return * table;
		}
		
		static const WaveTable& Create( const WaveTable& waveTable, double adjuster )
		{
			const WaveTable* tablePtr;
			switch ( waveTable.GetType() ) {
			case WaveTable::Type_Sine:
				{
					SineWaveTable table( waveTable.GetFrequency(), 0.0, adjuster );
					tablePtr = Get( table );
					if ( tablePtr != NULL  ) {
						return * tablePtr;
					}
					tablePtr = new SineWaveTable( waveTable.GetFrequency(), 0.0, adjuster );
				}
				break;
			case WaveTable::Type_Constant:
				{
					ConstantWaveTable table( waveTable.GetFrequency() );
					tablePtr = Get( table );
					if ( tablePtr != NULL  ) {
						return * tablePtr;
					}
					tablePtr = new ConstantWaveTable( waveTable.GetFrequency() );
				}
				break;
			case WaveTable::Type_Array:
				{
					const WaveTableWithArray& waveTableWithArray = 
						dynamic_cast< const WaveTableWithArray& >( waveTable );
					WaveTableWithArray table( waveTableWithArray.GetArray(), adjuster );
					tablePtr = Get( table );
					if ( tablePtr != NULL  ) {
						return * tablePtr;
					}
					tablePtr = new WaveTableWithArray( waveTableWithArray.GetArray(), adjuster );
				}
				break;
			case WaveTable::Type_Function:
				{
					const WaveTableWithWaveFunction& ref = dynamic_cast< const WaveTableWithWaveFunction& >( waveTable );
					WaveTableWithWaveFunction table( ref.GetWaveFunction(), adjuster );
					tablePtr = Get( table );
					if ( tablePtr != NULL  ) {
						return * tablePtr;
					}
					tablePtr = new WaveTableWithWaveFunction( table.GetWaveFunction(), adjuster );
				}
				break;
			default:
				throw Exception( "unknown type" );
			}

			waveTables.push_back( tablePtr );
			return * tablePtr;

		}
		
		static const WaveTable& Create( const WaveFunction& function, double adjuster )
		{
			WaveTableWithWaveFunction table( function, adjuster );
			const WaveTable* tablePtr = Get( table );
			if ( tablePtr != NULL ) {
				return *tablePtr;
			}
			tablePtr = new WaveTableWithWaveFunction( function, adjuster );
			waveTables.push_back( tablePtr );
			return *tablePtr;
		}

		static const WaveTable* Get( const WaveTable& table )
		{
			size_t count = waveTables.size();
			for ( int i = 0; i < count; i++ ) {
				if ( *waveTables[ i ] == table ) {
					return waveTables[ i ];
				}
			}
			return NULL;

		}

		static bool IsExist( const WaveTable& table ) 
		{
			if ( Get( table ) == NULL ) {
				return false;
			}
			return true;
		} 
	};

}